package oop.ex1.filescript;
import java.io.*;
import java.util.*;

import oop.ex1.actions.*;
import oop.ex1.filters.*;
import oop.ex1.orders.*;

public class MyFileScript {
	// Constants:
	final private static String ERROR_MESSAGE = "Error";
	final private static String YES = "Y";
	final private static String NO = "N";
	final private static String NOT = "NOT";
	final private static String UNDERSCORE = "_";
	final private static String DEFAULT_ORDER = "ABS";

	final private static char comment = '%';
	final private static String FILTER = "FILTER";
	final private static String ACTION = "ACTION";
	final private static String ORDER = "ORDER";
	// for date filters
	final private static boolean AFTER = true;
	final private static boolean BEFORE = false;
	final private static boolean GREATER = true;
	final private static boolean SMALLER = false;

	// data members:
	private static ArrayList<Subsection> _subsections;
	// enums:
	private enum FILTER_TYPE {
		BEFORE, AFTER, GREATER, SMALLER, FILE, READABLE, WRITABLE, EXECUTABLE
	};

	private enum ACTION_TYPE {
		PRINT, COPY, MOVE, REMOVE, EXEC, READ, WRITE
	};

	private enum ORDER_TYPE {
		ABS, FILE, MOD, SIZE
	};

	public static void main(String[] args) {
		try {
			if(areArgumentsLegal(args)){
				String path = parseAbsoluteFilePath(args[0]);
				String commandFile = parseAbsoluteFilePath(args[1]);
				ArrayList<Subsection> allSubsections;
				
					allSubsections = parser(new File(commandFile),path);
					if(allSubsections.isEmpty()){
						throw new IllegalArgumentException();
					}
					for (Subsection subsection : allSubsections) {
						subsection.DoAction(new File(path));
					}
			}
		}catch (IOException ioe) {
			System.exit(-1);
		} catch (IllegalArgumentException iae) {
			System.err.println(ERROR_MESSAGE);
			System.exit(-1);
		}
	}

	public static ArrayList<Subsection> parser(File commandFile,
			String directoryPath) throws IOException {
		FileReader input = null;
		BufferedReader reader = null;
		try {			
			input = new FileReader(commandFile);
			reader = new BufferedReader(input);
			_subsections = new ArrayList<Subsection>();
			String line = reader.readLine();
			while(line != null && commentChecker(line)){
				line=reader.readLine();
			}
			while (line != null) {
				Subsection sub = new Subsection();
				_subsections.add(sub);
				if (line.equals(FILTER)) {
					line = reader.readLine();
					if(commentChecker(line)){
						line=reader.readLine();
					}
					while (!line.equals(ACTION)) {
						if(commentChecker(line)){
							line = reader.readLine();
							continue;
						}
						filterLineParser(line);
						line = reader.readLine();
					}
				}
				line = reader.readLine();
				while (line != null && !line.equals(ORDER) && !line.equals(FILTER)) {
					if(commentChecker(line)){
						line = reader.readLine();
						continue;
					}
					_subsections.get(_subsections.size()-1).
					addAction(actionParser(line,directoryPath));
					line = reader.readLine();
				}
				if(line == null || line.equals(FILTER)){
					_subsections.get(_subsections.size()-1).
					addOrder(orderParser(DEFAULT_ORDER));
				}else{
					//if (hasLegalActions(_subsections.get(_subsections.size()-1).getActions())){
						line = reader.readLine();
						while (line!=null&&!line.equals(FILTER)) {
							if(commentChecker(line)){
								line = reader.readLine();
								continue;
							}
							_subsections.get(_subsections.size()-1).
							addOrder(orderParser(line));
							line = reader.readLine();
					//	}
					}
				}
			if (hasLegalActions(_subsections.get(_subsections.size()-1).getActions()));
			}
		} catch (FileNotFoundException e) {
			throw new IOException();
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return _subsections;
	}

	private static void filterLineParser(String line) {
		// checks whether the filter section is empty,i.e. "FILTER\nACTION"
		if (line.equals(ACTION)) {
			return;
		}
		String[] allFilters = line.split("\\s+");
		_subsections.get(_subsections.size()-1).addFilterColumn();
		for (String currentFilter : allFilters) {
			_subsections.get(_subsections.size()-1).
			addFilter(filterParser(currentFilter));
		}
	}

	private static Filter filterParser(String line) {
		String[] filterComponents = parseLine(line);
		switch (FILTER_TYPE.valueOf(filterComponents[0])) {
		case BEFORE:
			return new DateFilter(BEFORE,isNotSuffix(filterComponents),
					filterComponents[1]);
		case AFTER:
			return new DateFilter(AFTER,isNotSuffix(filterComponents),
					filterComponents[1]);
		case GREATER:
			return new SizeFilter(GREATER,isNotSuffix(filterComponents),
					filterComponents[1]);
		case SMALLER:
			return new SizeFilter(SMALLER,isNotSuffix(filterComponents),
					filterComponents[1]);
		case FILE:
			return new NameFilter(filterComponents[1],
					isNotSuffix(filterComponents));
		case READABLE:
			return new DiskAttributesFilter('r',
					isNotSuffix(filterComponents),
					yesNoValidation(filterComponents[1]));

		case WRITABLE:
			return new DiskAttributesFilter('w',
					isNotSuffix(filterComponents),
					yesNoValidation(filterComponents[1]));
		case EXECUTABLE:
			return new DiskAttributesFilter('x',
					isNotSuffix(filterComponents),
					yesNoValidation(filterComponents[1]));
		}
		return null;
	}

	//checks whether the additional suffixes except Y/N exist,
	//and if they are, are they legal i.e. one _NOT appearance
	private static boolean isNotSuffix(String[] filterComponents) {
		if (filterComponents.length > 3) {
			throw new IllegalArgumentException();
		}else{
			if(filterComponents.length==3){
				if (filterComponents[2].equals(NOT)) {
					return true;
				}else{
					return false;
				}
			}
			return false;
		}
	}
	
	private static Action actionParser(String line,String directoryPath) {
		//TODO check for code repetition with the split
		String[] actionComponents = parseLine(line);
		switch (ACTION_TYPE.valueOf(actionComponents[0])) {
		case PRINT:
			return new PrintAction();
		case COPY:
			return new CopyAction(directoryPath, actionComponents[1]);
		case MOVE:
			return new MoveAction(directoryPath, actionComponents[1]);
		case REMOVE:
			return new RemoveAction();
		case READ:
			return new AttribAction('r', yesNoValidation(actionComponents[1]));
		case WRITE:
			return new AttribAction('w', yesNoValidation(actionComponents[1]));
		case EXEC:
			return new AttribAction('x', yesNoValidation(actionComponents[1]));
		}
		return null;
	}

	private static Comparator<File> orderParser(String line) {
		switch (ORDER_TYPE.valueOf(line)) {
		case ABS:
			return new AbsOrder();
		case FILE:
			return new FileOrder();
		case MOD:
			return new ModOrder();
		case SIZE:
			return new SizeOrder();
		}
		return null;
	}

	// if the line begins with a "%" char, then it's a comment, and it's
	// saved to the comments arrayList.
	//return true if line is a comments, false otherwise.
	private static boolean commentChecker(String line) {
		if(line.equals("")){
			return true;
		}
		if (line.charAt(0) == comment) {
			_subsections.get(_subsections.size()-1).addComment(line);
			return true;
		}else{
			return false;
		}
	}
	
	

	private static boolean areArgumentsLegal(String[] args) {
		if (args.length != 2) {
			throw new IllegalArgumentException();
		}
		if (!new File(args[0]).isDirectory() || !new File(args[1]).isFile()) {
			throw new IllegalArgumentException();
		} else {
			return true;
		}
	}

	 // checks whether the ACTION section is technically fine, i.e. not empty
	 // or MOVE/REMOVE followed by other actions
	 // @param actionList
	 // @return false - if there are no actions in the ACTION section, or the
	 // MOVE/REMOVE action is followed by an other action true - otherwise
	private static boolean hasLegalActions(ArrayList<Action> actionList) {
		if (actionList.isEmpty()) {
			throw new IllegalArgumentException();
		}
		for (Action action : actionList) {
			if (action instanceof MoveAction || action instanceof RemoveAction) {
				if (actionList.indexOf(action) != actionList.size() - 1) {
					throw new IllegalArgumentException();
				}
			}
		}
		return true;
	}

	// checks whether the argument for the Readable/Writable/Executable
	// filter/action is legal, i.e. Y or N
	private static boolean yesNoValidation(String argument) {
		if (!argument.equals(YES) && !argument.equals(NO)) {
			throw new IllegalArgumentException();
		} else {
			if(argument.equals(YES)){
				return true;
			}else{
				return false;
			}
		}
	}
	
	//helper method to find the absolute path for a file name 
	//whether it's relative or absolute
	private static String parseAbsoluteFilePath(String fileName) {
		File file = new File(fileName);
		if(file.isAbsolute()){
			return fileName;
		}
		else{
			String fullPath = file.getAbsolutePath();
			return fullPath;
		}
	}
	
	private static String[] parseLine(String line){
		if(line.lastIndexOf(UNDERSCORE)==line.length()-1){
			throw new IllegalArgumentException();
		}
		String[] result = line.split(UNDERSCORE);
		int index = 0;
		for(String segment : result){
			if(segment.equals("")){
				throw new IllegalArgumentException();
			}
			index++;
		}
		return result;
	}
}
